//
// Created by denglibin on 19-4-12.
//
/**
 * 1)使用epoll()创建一个epoll上下文
 *      #include <sys/epoll.h>
        int epoll_create (int size)
        调用成功，epoll_create()创建一个epoll实例，返回与该实例相关的文件描述符
        这个文件描述符和真正的文件没有关系，仅仅是为了后续调用使用epoll而创建
        size:告诉内核需要监听的文件描述符的数目，但不是最大值，传递一个适当的近似值会带来性能的提升，
        但不需要给出确定的数字，出错时，返回-1,并设置errno。
        errno的值：
        EINVAL:size不是正数
        ENFILE:系统达到打开文件数的上限
        ENOMEN: 没有足够的内存来完成该次操作。

        一个标准的调用如下：
        int epfd;
        epfd = epoll_create (100); //plan to watch  ̃100 fds
        if (epfd < 0) perror (”epoll_create”);
        epoll()创建的文件描述符需要调用close()关闭

    2)控制epoll
        epoll_ctl() 可以向指定的epoll上下文中添加或删除文件描述符：
        #include <sys/epoll.h>
        int epoll_ctl (int epfd, int op, int fd, struct
        epoll_event *event);
        参数op:指定要对fd进行的操作
        event:描述epoll更具体的行为
        op的有效值：
        EPOLL_CTL_ADD:把fd指定的文件描述符添加到epfd指定的epoll实例监听集中，监听event中定义的事件。
        EPOLL_CTL_DEL:把fd指定的文件从epfd指定的epoll监听集中删除。
        EPOLL_CTL_MOD:使用event改变已有fd上的监听行为。

        event的值：
        events可以是以下几个宏的集合：
        EPOLLIN ：表示对应的文件描述符可以读（包括对端SOCKET正常关闭）；
        EPOLLOUT：表示对应的文件描述符可以写；
        EPOLLPRI：表示对应的文件描述符有紧急的数据可读（这里应该表示有带外数据到来）；
        EPOLLERR：表示对应的文件描述符发生错误；
        EPOLLHUP：表示对应的文件描述符被挂断；
        EPOLLET： 将EPOLL设为边缘触发(Edge Triggered)模式，这是相对于水平触发(Level Triggered)来说的。
        EPOLLONESHOT：只监听一次事件，当监听完这次事件之后，如果还需要继续监听这个socket的话，需要再次把这个socket加入到EPOLL队列里

    3） int epoll_wait(int epfd, struct epoll_event * events, int maxevents, int timeout);
        等待事件的产生，类似于select()调用。
        参数events用来从内核得到事件的集合，
        maxevents告之内核这个events有多大，这个 maxevents的值不能大于创建epoll_create()时的size，
        参数timeout是超时时间（毫秒，0会立即返回，-1将不确定，也有说法说是永久阻塞）。
        该函数返回需要处理的事件数目，如返回0表示已超时

 */
#include <stdio.h>
#include <unistd.h>
#include <sys/epoll.h>
#define  BUF_LEN 1024
static int main_(void){

    int epfd, nfds;
    struct epoll_event ev, events[5];//ev用于注册事件，数组用于返回要处理的事件
    epfd = epoll_create(1);//只需要监听一个描述符——标准输入
    ev.data.fd = STDIN_FILENO; //要监听的文件描述符
    ev.events = EPOLLIN | EPOLLET;//监听的实践 监听读状态同时设置ET模式 边缘触发(默认水平触发 linux系统编程4.2.4)

    epoll_ctl(epfd, EPOLL_CTL_ADD, STDOUT_FILENO, &ev);
    if (1){ //可以改为while循环监听
        nfds = epoll_wait(epfd, events, 5, -1);
        for(int i = 0; i < nfds;i++){
            if(events[i].data.fd == STDIN_FILENO){
                char buf[BUF_LEN+1];
                int len = read(STDIN_FILENO, buf, BUF_LEN);
                if(len == -1){
                    perror("read");
                    return -1;
                }
                if(len){
                    buf[len] = '\0';
                    printf("read: %s\n", buf);
                }
            }
        }
    }
    close(epfd);
    return 0;
}























